{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "递归式特征消除Recursive feature elimination(RFE)\n",
    "\n",
    "\n",
    "给定一个为特征(如线性模型的系数)分配权重的外部估计量，递归特征消除([RFE](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFE.html#sklearn.feature_selection.RFE))就是通过递归地考虑越来越小的特征集来选择特征。首先，对初始特征集训练估计器，通过coef_属性或feature_importances_属性获得每个特征的重要性。然后，从当前的特征集中删除最不重要的特征。在经过修剪的集合上递归地重复这个过程，直到最终达到所需的特征数量。\n",
    "说简单点，递归式特征消除的主要思路是反复建立多种模型，每一次根据系数的不挑出差的特征，并去除挑出来的特征，然后在剩余的特征上重复该过程，直到遍历了所有的特征。所以递归式特征消除效果如果很看选用的模型。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 多行输出\r\n",
    "from IPython.core.interactiveshell import InteractiveShell\r\n",
    "InteractiveShell.ast_node_interactivity = \"all\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 1  基本使用介绍"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "对于RFE函数，主要参数如下：\n",
    "+ estimator：一种监督学习估计器，其fit方法通过coef_ 属性或feature_importances_属性提供有关要素重要性的信息\n",
    "+ n_features_to_select：要保留的特征数量，默认保留一半\n",
    "+ step：为整数时表示每次要删除的特征数量；小于1时表示每次去除权重最小的特征，默认为1\n",
    "\n",
    "以下示例说明了，如何通过RFE函数挑选5个最佳特征"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ True  True  True  True  True False False False False False]\n",
      "[1 1 1 1 1 6 4 3 2 5]\n",
      "5\n"
     ]
    }
   ],
   "source": [
    "from sklearn.datasets import make_friedman1\r\n",
    "from sklearn.feature_selection import RFE\r\n",
    "from sklearn.svm import SVR\r\n",
    "X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)\r\n",
    "estimator = SVR(kernel=\"linear\")\r\n",
    "selector = RFE(estimator, n_features_to_select=5, step=1)\r\n",
    "selector = selector.fit(X, y)\r\n",
    "\r\n",
    "# 哪些特征入选最后特征，true表示入选\r\n",
    "print(selector.support_)\r\n",
    "\r\n",
    "# 每个特征的得分排名，特征得分越低（1最好），表示特征越好\r\n",
    "print(selector.ranking_)\r\n",
    "\r\n",
    "#  挑选了几个特征\r\n",
    "print(selector.n_features_)\r\n",
    "\r\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "sklearn中[RFECV](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html#sklearn.feature_selection.RFECV)函数在交叉验证循环中执行RFE，以找到最佳数量的特征。RFE的稳定性很大程度上取决于迭代时用的哪种模型。RFECV 通过交叉验证的方式来执行RFE。\n",
    "RFE需要一个指定数量的特性来保留，但是通常事先不知道有多少特性是有效的。为了寻找最佳特征数，采用RFE对不同特征子集进行交叉验证，并选择出最优的特征评分集合，但是如果删除特征会导致性能损失就不删除特征。这就是RFECV的原理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "对于RFECV函数，主要参数如下：\r\n",
    "+ estimator：一种监督学习估计器，其fit方法通过coef_ 属性或feature_importances_属性提供有关要素重要性的信息\r\n",
    "+ step：为整数时表示每次要删除的特征数量；小于1时表示每次去除权重最小的特征，默认为1\r\n",
    "+ min_features_to_select：保留的最少的特征数（但是如果模型有特征数量限制，如随机森林设置了最大特征数，该变量需要大于等于随机森林设定的最大特征数），默认为1。\r\n",
    "+ cv：指定交叉验证的折数，默认为5\r\n",
    "\r\n",
    "以下示例说明了，如何通过RFECV挑选特征。如果减少特征会造成性能损失，那么将不会去除任何特征"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ True  True  True  True  True False False False False False]\n",
      "[1 1 1 1 1 6 4 3 2 5]\n",
      "5\n",
      "[0.2119209  0.34014969 0.32498071 0.39840786 0.44286114 0.42111785\n",
      " 0.38499244 0.393591   0.39398469 0.38667796]\n"
     ]
    }
   ],
   "source": [
    "from sklearn.datasets import make_friedman1\r\n",
    "from sklearn.feature_selection import RFECV\r\n",
    "from sklearn.svm import SVR\r\n",
    "\r\n",
    "# 生成样本\r\n",
    "# X维度(50，20)，Y维度(50,1)\r\n",
    "X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)\r\n",
    "\r\n",
    "estimator = SVR(kernel=\"linear\")\r\n",
    "\r\n",
    "# 5折交叉\r\n",
    "selector = RFECV(estimator, step=1, cv=5)\r\n",
    "selector = selector.fit(X, y)\r\n",
    "\r\n",
    "# 哪些特征入选最后特征，true表示入选\r\n",
    "print(selector.support_)\r\n",
    "\r\n",
    "# 每个特征的得分排名，特征得分越低（1最好），表示特征越好\r\n",
    "print(selector.ranking_)\r\n",
    "\r\n",
    "#  挑选了几个特征\r\n",
    "print(selector.n_features_)\r\n",
    "# 每次交叉迭代各个特征得分\r\n",
    "print(selector.grid_scores_)\r\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "\n",
    "详细来说K折交叉验证，就是将数据集等比例划分成K份，以其中的一份作为测试数据，其他的K-1份数据作为训练数据。交叉验证实际是把实验重复做了K次，每次实验都是从K个部分选取一份不同的数据部分作为测试数据（保证K个部分的数据都分别做过测试数据），剩下的K-1个当作训练数据，最后把得到的K个实验结果进行平分。然是RFECV不是这样的，RFEC由RFE和CV(Cross-validation)组成，\n",
    "RFECV源代码如下，在每次实验针对部分特征进行RFE计算。我们在所有CV上保留每个功能得分的平均值。然后，我们使用平均得分计算要删除的要素数量，然后使用整个数据集删除该数量的要素，这就是源代码所表达的意思。\n",
    "举个例子如果有a，b，c三个特征，交叉认证每次提取部分特征，比如第一次提取特征(a,b)与y建模，计算在测试集的得分。第二次提取特征(a,c)进行建模，第三次对（a,b,c）进行建模。如5折交叉验证会得到5个分数数组，会对5个分数数组进行对应元素求和，得到各个特征数量下的总分，求出最高总分，以及多少特征才能达到最高分，那么就可以求得应该删多少特征，然后针对整个数据进行RFE。\n",
    "\n",
    "```\n",
    "# 提取X,y\n",
    "X, y = check_X_y(X, y, \"csr\", ensure_min_features=2)\n",
    "\n",
    "# Initialization\n",
    "# k折交叉\n",
    "cv = check_cv(self.cv, y, is_classifier(self.estimator))\n",
    "scorer = check_scoring(self.estimator, scoring=self.scoring)\n",
    "n_features = X.shape[1]\n",
    "\n",
    "if 0.0 < self.step < 1.0:\n",
    "    step = int(max(1, self.step * n_features))\n",
    "else:\n",
    "    step = int(self.step)\n",
    "if step <= 0:\n",
    "    raise ValueError(\"Step must be >0\")\n",
    "\n",
    "# Build an RFE object, which will evaluate and score each possible\n",
    "# feature count, down to self.min_features_to_select\n",
    "rfe = RFE(estimator=self.estimator,\n",
    "          n_features_to_select=self.min_features_to_select,\n",
    "          step=self.step, verbose=self.verbose)\n",
    "\n",
    "# Determine the number of subsets of features by fitting across\n",
    "# the train folds and choosing the \"features_to_select\" parameter\n",
    "# that gives the least averaged error across all folds.\n",
    "\n",
    "# Note that joblib raises a non-picklable error for bound methods\n",
    "# even if n_jobs is set to 1 with the default multiprocessing\n",
    "# backend.\n",
    "# This branching is done so that to\n",
    "# make sure that user code that sets n_jobs to 1\n",
    "# and provides bound methods as scorers is not broken with the\n",
    "# addition of n_jobs parameter in version 0.18.\n",
    "\n",
    "if effective_n_jobs(self.n_jobs) == 1:\n",
    "    parallel, func = list, _rfe_single_fit\n",
    "else:\n",
    "    parallel = Parallel(n_jobs=self.n_jobs)\n",
    "    func = delayed(_rfe_single_fit)\n",
    "\n",
    "# k折交叉认证\n",
    "scores = parallel(\n",
    "    func(rfe, self.estimator, X, y, train, test, scorer)\n",
    "    for train, test in cv.split(X, y, groups))\n",
    "\n",
    "# 计算各个交叉认证下各个元素的分数之和\n",
    "scores = np.sum(scores, axis=0)\n",
    "scores_rev = scores[::-1]\n",
    "# 判断第几次交叉认证取得评分最大值\n",
    "argmax_idx = len(scores) - np.argmax(scores_rev) - 1\n",
    "# 根据设定参数step，到argmax_idx每次减少step个元素，这样能求出保留几个袁术\n",
    "n_features_to_select = max(\n",
    "    n_features - (argmax_idx * step),\n",
    "    self.min_features_to_select)\n",
    "\n",
    "# Re-execute an elimination with best_k over the whole set\n",
    "rfe = RFE(estimator=self.estimator,\n",
    "          n_features_to_select=n_features_to_select, step=self.step,\n",
    "          verbose=self.verbose)\n",
    "\n",
    "rfe.fit(X, y)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 2 应用实例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "下面的实例为一个递归特征消除RFE函数示例，显示了数字分类任务中像素的重要性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练图像集的维度 (1797, 64)\n",
      "图像各个像素点的重要性排名： [64 50 31 23 10 17 34 51 57 37 30 43 14 32 44 52 54 41 19 15 28  8 39 53\n",
      " 55 45  9 18 20 38  1 59 63 42 25 35 29 16  2 62 61 40  5 11 13  6  4 58\n",
      " 56 47 26 36 24  3 22 48 60 49  7 27 33 21 12 46]\n"
     ]
    }
   ],
   "source": [
    "from sklearn.svm import SVC\r\n",
    "from sklearn.datasets import load_digits\r\n",
    "from sklearn.feature_selection import RFE\r\n",
    "import matplotlib.pyplot as plt\r\n",
    "\r\n",
    "# Load the digits dataset\r\n",
    "# 读取数据集\r\n",
    "digits = load_digits()\r\n",
    "X = digits.images.reshape((len(digits.images), -1))\r\n",
    "y = digits.target\r\n",
    "print(\"训练图像集的维度\",X.shape)\r\n",
    "\r\n",
    "# Create the RFE object and rank each pixel\r\n",
    "svc = SVC(kernel=\"linear\", C=1)\r\n",
    "# n_features_to_select=1表示每次都删除一个特征。比如X的图像为8*8的尺寸。共64个像素，对64个像素都进行排名\r\n",
    "rfe = RFE(estimator=svc, n_features_to_select=1, step=1)\r\n",
    "rfe.fit(X, y)\r\n",
    "print(\"图像各个像素点的重要性排名：\",rfe.ranking_)\r\n",
    "# 大小重置\r\n",
    "ranking = rfe.ranking_.reshape(digits.images[0].shape)\r\n",
    "\r\n",
    "\r\n",
    "# Plot pixel ranking\r\n",
    "# 颜色越浅表明该像素点对于手写数字图像分类越重要\r\n",
    "plt.matshow(ranking, cmap=plt.cm.Blues)\r\n",
    "plt.colorbar()\r\n",
    "plt.title(\"Ranking of pixels with RFE\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "下面为一个递归特征消除示例，该示例通过交叉验证自动调整所选特征的数量。最好画出选用各个特征数量下，分类集的交叉认证分数。可以看到RFECV能够自动选择适合分类的有效特征数\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X维度 (1000, 25)\n",
      "y维度 (1000,)\n",
      "RFEC挑选了几个特征 : 3\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\r\n",
    "from sklearn.svm import SVC\r\n",
    "from sklearn.model_selection import StratifiedKFold\r\n",
    "from sklearn.feature_selection import RFECV\r\n",
    "from sklearn.datasets import make_classification\r\n",
    "\r\n",
    "# Build a classification task using 3 informative features\r\n",
    "# 建立特征，X有25个特征，其中有效特征3个。\r\n",
    "X, y = make_classification(n_samples=1000, n_features=25, n_informative=3,\r\n",
    "                           n_redundant=2, n_repeated=0, n_classes=8,\r\n",
    "                           n_clusters_per_class=1, random_state=0)\r\n",
    "print(\"X维度\", X.shape)\r\n",
    "print(\"y维度\",y.shape)\r\n",
    "\r\n",
    "\r\n",
    "# Create the RFE object and compute a cross-validated score.\r\n",
    "# 创建分类器\r\n",
    "svc = SVC(kernel=\"linear\")\r\n",
    "# The \"accuracy\" scoring is proportional to the number of correct\r\n",
    "# classifications\r\n",
    "# 分类\r\n",
    "rfecv = RFECV(estimator=svc, step=1, cv=StratifiedKFold(2),\r\n",
    "              scoring='accuracy')\r\n",
    "rfecv.fit(X, y)\r\n",
    "\r\n",
    "print(\"RFEC挑选了几个特征 : %d\" % rfecv.n_features_)\r\n",
    "\r\n",
    "# Plot number of features VS. cross-validation scores\r\n",
    "# 画出不同特征数量下交叉认证验证得分\r\n",
    "plt.figure()\r\n",
    "#  选择的特征数量\r\n",
    "plt.xlabel(\"Number of features selected\")\r\n",
    "# 交叉验证得分\r\n",
    "plt.ylabel(\"Cross validation score (nb of correct classifications)\")\r\n",
    "# 画出各个特征的得分\r\n",
    "plt.plot(range(1, len(rfecv.grid_scores_) + 1), rfecv.grid_scores_)\r\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 3 参考\n",
    "\n",
    "> [https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFE.html#sklearn.feature_selection.RFE](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFE.html#sklearn.feature_selection.RFE)\n",
    "\n",
    "> [https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html#sklearn.feature_selection.RFECV](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFECV.html#sklearn.feature_selection.RFECV)\n",
    "\n",
    "> [http://www.minxueyu.com/2020/03/29/RFE%E4%B8%8ERFECV%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/](http://www.minxueyu.com/2020/03/29/RFE%E4%B8%8ERFECV%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/)\n",
    "\n",
    "> [https://blog.csdn.net/sunshunli/article/details/82355395](https://blog.csdn.net/sunshunli/article/details/82355395)\n",
    "\n",
    "> [https://stackoverflow.com/questions/34703051/score-of-rfecv-in-python-scikit-learn](https://stackoverflow.com/questions/34703051/score-of-rfecv-in-python-scikit-learn)\n",
    "\n",
    "> [https://blog.csdn.net/teng_zz/article/details/98027712](https://blog.csdn.net/teng_zz/article/details/98027712)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 1.8.0 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
